package com.xlhl.init.manage;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.batch.BatchMethod;
import com.baomidou.mybatisplus.core.batch.MybatisBatch;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 针对 Mybatis Plus 批量操作封装 E: 实体类型  T: 待转换的类型
 * ByConvert 后缀就是多了一步：将其他数据类型转换为 实体类型的操作
 * 没有事务控制，需要事务需要自己操作
 *
 * <pre>
 *  Spring示例:
 *
 *      {@link org.springframework.transaction.support.TransactionTemplate}
 *      private TransactionTemplate transactionTemplate;
 *
 * 		transactionTemplate.execute((TransactionCallback<List<BatchResult>>) status -> {
 *          MybatisBatch.Method<H2User> mapperMethod = new MybatisBatch.Method<>(H2UserMapper.class);
 *          // 执行批量插入
 *          MybatisBatchUtils.execute(sqlSessionFactory, h2UserList, mapperMethod.insert());
 *          throw new RuntimeException("出错了");
 *      });
 * </pre>
 *
 * @author xlhl
 */
@Component
public class MyBatisBatchManage {
    @Resource
    private SqlSessionFactory sqlSessionFactory;

    /**
     * 批量操作（不对外开放）
     *
     * @param dataList 数据列表（实体类型）
     * @param method   操作
     * @return
     */
    private <E> List<BatchResult> batchAction(List<E> dataList, BatchMethod<E> method) {
        if (CollUtil.isEmpty(dataList)) {
            return Collections.emptyList();
        }

        return new MybatisBatch<>(sqlSessionFactory, dataList).execute(method);
    }

    /**
     * 批量新增
     *
     * @param dataList    数据列表（实体类型）
     * @param mapperClass 实体对应 Mapper 的 class
     * @return
     */
    public <E> List<BatchResult> batchInsert(List<E> dataList, Class<? extends BaseMapper<E>> mapperClass) {
        return batchAction(dataList, new MybatisBatch.Method<E>(mapperClass).insert());
    }

    /**
     * 批量新增
     *
     * @param dataList 数据列表（非实体类型）
     * @param clazz    实体对应 Mapper 的 class
     * @param func     把 非实体类型转换为 实体类型
     * @return
     */
    public <T, E> List<BatchResult> batchInsertByConvert(List<T> dataList, Class<? extends BaseMapper<E>> clazz, Function<T, E> func) {
        return batchInsert(convertList(dataList, func), clazz);
    }

    /**
     * 批量修改
     *
     * @param dataList    数据列表（实体类型）
     * @param mapperClass 实体对应 Mapper 的 class
     * @return
     */
    public <E> List<BatchResult> batchUpdate(List<E> dataList, Class<? extends BaseMapper<E>> mapperClass) {
        return batchAction(dataList, new MybatisBatch.Method<E>(mapperClass).updateById());
    }

    /**
     * 批量修改
     *
     * @param dataList 数据列表（非实体类型）
     * @param clazz    实体对应 Mapper 的 class
     * @param func     把 非实体类型转换为 实体类型
     * @return
     */
    public <T, E> List<BatchResult> batchUpdateByConvert(List<T> dataList, Class<? extends BaseMapper<E>> clazz, Function<T, E> func) {
        return batchUpdate(convertList(dataList, func), clazz);
    }

    /**
     * 批量删除
     *
     * @param dataList    数据列表（实体类型）
     * @param mapperClass 实体对应 Mapper 的 class
     * @return
     */
    public <E> List<BatchResult> batchDelete(List<E> dataList, Class<? extends BaseMapper<E>> mapperClass) {
        return batchAction(dataList, new MybatisBatch.Method<E>(mapperClass).deleteById());
    }

    /**
     * 批量删除
     *
     * @param dataList 数据列表（非实体类型）
     * @param clazz    实体对应 Mapper 的 class
     * @param func     把 非实体类型转换为 实体类型
     * @return
     */
    public <T, E> List<BatchResult> batchDeleteByConvert(List<T> dataList, Class<? extends BaseMapper<E>> clazz, Function<T, E> func) {
        return batchDelete(convertList(dataList, func), clazz);
    }

    /**
     * 列表转换 T -> E
     *
     * @param list
     * @param func
     * @param <T>
     * @param <E>
     * @return
     */
    private <T, E> List<E> convertList(List<T> list, Function<T, E> func) {
        return list.stream().map(func).collect(Collectors.toList());
    }
}














